package relation;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import my_exception.AccessException;
import my_exception.checkException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
 * 表结构
 * @author cgl-22341129
 */
public class Table {
    private String name;
    private String rightcontrol;

    /**
     * 标记为非临时表:1/临时表:2
     * 该标记不会被序列化/反序列化
     */
    @JSONField(deserialize = false,serialize = false)
    private int flag=1;
    private ArrayList<Field> fields;
    private ArrayList<JSONObject> tuple;

    public void setTempTable() {
        this.flag = 2;
    }

    public int getFlag() {
        return flag;
    }

    public void setTuple(ArrayList<JSONObject> tuple) {
        this.tuple = tuple;
    }

    public ArrayList<JSONObject> getTuple() {
        return tuple;
    }
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public String getRightcontrol() {
        return rightcontrol;
    }

    public void setRightcontrol(String rightcontrol) {
        this.rightcontrol = rightcontrol;
    }

    public void setFields(ArrayList<Field> fields) {
        this.fields = fields;
    }

    public ArrayList<Field> getFields() {
        return fields;
    }

    /**
     * print表的creat信息
     */
    public void showcreattable(){
        System.out.println(name);
        System.out.println(rightcontrol);
        System.out.print("字段名  数据类型  键  默认值  外键约束");
        System.out.println();
        for(Field field:fields) {
            field.showfield();
        }
    }

    public Table(String name, String rightcontrol, ArrayList<Field> fields, ArrayList<JSONObject> tuple) {
        this.flag=1;
        this.name = name;
        this.rightcontrol = rightcontrol;
        this.fields = fields;
        this.tuple = tuple;
    }

    /**
     * JSON类转化为Table类
     * @param jsonObject
     * @return Table
     */
    static public Table JSONObjectToTable(JSONObject jsonObject){
        JSONArray jsonArray=jsonObject.getJSONArray("fields");
        ArrayList<Field> fields=new ArrayList<Field>();
        for(int i=0;i<jsonArray.size();i++){
            fields.add(JSONObject.toJavaObject((JSONObject)jsonArray.get(i),Field.class));
        }

        jsonArray=jsonObject.getJSONArray("tuple");
        ArrayList<JSONObject> tuple=new ArrayList<JSONObject>();
        for(int i=0;i<jsonArray.size();i++){
            tuple.add((JSONObject)jsonArray.get(i));
        }
        return new Table(jsonObject.getString("name"),jsonObject.getString("rightcontrol"),fields,tuple);
    }
    /**
     * print出table所有内容
     */
    public void showtable(){
        System.out.println(name+":");
        for(Field field:fields)
            System.out.print(field.getFieldname()+"  ");
        System.out.println();

        for(JSONObject jsonObject:tuple){
            for(Field field:fields){
                System.out.print(jsonObject.get(field.getFieldname())+"   ");
            }
            System.out.println();
        }
    }

    /**
     * 静态showtable内容，可以用于打印中间表内容
     * @param table 需要打印的表
     */
    static void showtable(Table table){
        table.showtable();
    }

    /**
     * 检查元组的属性是否符合表定义，抛出异常
     */
    public void checkTuplesFieldname()throws checkException {

        Set<String> tuplefield;
        ArrayList<String> fieldnames=new ArrayList<>();
        for(Field field:fields)
            fieldnames.add(field.getFieldname());

        int i=0;
        for(JSONObject jsonObject:tuple){
           tuplefield=jsonObject.keySet();

           for(String s:fieldnames){
               if(!tuplefield.contains(s)){
                   throw new checkException("表"+this.getName()+"中的第"+i+"个"+"元组中缺少字段，缺少字段为"+s);
               }
           }

           for(String s:tuplefield){
               if(!fieldnames.contains(s)){
                   throw new checkException("表"+this.getName()+"中的第"+i+"个"+"元组中出现了多余的字段，多余的字段为:"+s);
               }
           }
           i++;
        }

    }
    /**
     * 给出index编号，以及字段名，判断是否相等
     * @param index 索引
     * @param fieldname 属性名
     * @param value 属性值
     * @return 是返回true,否返回false
     * @throws AccessException 下标越界时抛出错误
     */
    public boolean isequals(int index,String fieldname,Object value) throws AccessException {
        if(index>=tuple.size()){
            throw new AccessException("访问tuple时下标越界");
        }
        return value.equals(tuple.get(index).get(fieldname));
    }

    /**
     * 返回满足 fieldname==value的所有元组的索引值
     * @param fieldname 字段名
     * @param value 寻找的key值
     * @return 所有满足条件的索引值
     */
    public ArrayList<Integer> find(String fieldname,Object value)  {
        ArrayList<Integer> index=new ArrayList<Integer>();
        for(int i=0;i<this.tuple.size();i++){
            try {
                if(isequals(i,fieldname,value)){
                    index.add(i);
                }
            } catch (AccessException e) {
                throw new RuntimeException(e);
            }
        }
     return index;
    }

    /**
     * 在给出的元组索引值集中搜索符合条件的元组
     * @param ind 索引值集
     * @param fieldname 字段名
     * @param value 寻找的key值
     * @return 所有满足条件的索引值
     */
    public ArrayList<Integer> find(ArrayList<Integer> ind,String fieldname,Object value){
        ArrayList<Integer> index=new ArrayList<Integer>();
        for(int i:ind){
            try {
                if(isequals(i,fieldname,value)){
                    index.add(i);
                }
            } catch (AccessException e) {
                throw new RuntimeException(e);
            }
        }
        return index;
    }
    /**
     * 更新索引对应元组值
     * @param index 索引值
     * @param fieldname 将要更改字段名
     * @param newValue 更新的值
     * @throws AccessException 错误抛出
     */
    public void updatetuple(int index,String fieldname,Object newValue) throws AccessException {
        if(!tuple.get(index).keySet().contains(fieldname)){
            throw new AccessException("试图修改tuple中不存在的属性");
        }
        tuple.get(index).put(fieldname,newValue);
    }
    /**
     * 求两表笛卡尔积,并符以默认值
     * @param table1 表1
     * @param table2 表2
     * @return 返回的笛卡尔积
     */
    static public Table CarProduct(Table table1,Table table2) {
        return CarProduct(table1, table2, "temptable"+ Tempid.getId());
    }

    /**
     * 求两表笛卡尔积,并记别名
     * @param table1 表1
     * @param table2 表2
     * @param alias 别名
     * @return 新的表类
     */
    static public Table CarProduct(Table table1, Table table2, String alias){
        ArrayList<Field> newfields=new ArrayList<Field>();
        ArrayList<JSONObject> newtuple=new ArrayList<JSONObject>();
        newfields.addAll(table1.getFields());

        for(Field field:table2.getFields()){

            if(Field.contains(newfields,field.getFieldname())){
                Field rfield=new Field(field);
                rfield.setFieldname(table2.getName()+"_"+field.getFieldname());
                newfields.add(rfield);

            }
            else {
                newfields.add(field);
            }
        }
        JSONObject newObject;
        for(JSONObject tuple1:table1.getTuple()){
            for(JSONObject tuple2:table2.getTuple()){

             newObject=new JSONObject();

                for (Map.Entry<String, Object> entry : tuple1.entrySet()) {
                    newObject.put(entry.getKey(),entry.getValue());

                }
                for(Map.Entry<String, Object> entry : tuple2.entrySet()){
                    if(Field.contains(table1.getFields(),entry.getKey())){
                        newObject.put(table2.getName()+"_"+entry.getKey(),entry.getValue());
                    }else {
                        newObject.put(entry.getKey(),entry.getValue());
                    }

                }
                newtuple.add(newObject);
            }
        }
        Table newtable= new Table(alias,"临时",newfields,newtuple);
        try {
            newtable.checkTuplesFieldname();
        } catch (checkException e) {
            throw new RuntimeException(e);
        }

        newtable.setTempTable();
        return newtable;
    }

    /**
     * 判断元组是否与当前表匹配
     * @param newtuple 即将插入的元组
     * @return
     */
    public boolean tuplematch(JSONObject newtuple){
        //先检查插入的元素是否满足表结构
        Set<String> tuplefields=newtuple.keySet();
        for(Field field:fields){
            if(!tuplefields.contains(field.getFieldname())){
                return false;
            }
        }
        for(String s:tuplefields){
            if(!Field.contains(this.fields,s)){
                return false;
            }
        }
        return true;
    }

    /**
     * 插入元组，并检查元组是否合理
     * @param newtuple 需要插入的元组
     * @throws checkException 抛出元组与表的匹配错误
     */
    public void insert(JSONObject newtuple) throws checkException {
        //先检查插入的元素是否满足表结构
        Set<String> tuplefields=newtuple.keySet();
        for(Field field:fields){
            if(!tuplefields.contains(field.getFieldname())){
                throw new checkException("插入元组缺少属性"+field.getFieldname());
            }
        }
        for(String s:tuplefields){
            if(!Field.contains(this.fields,s)){
                throw new checkException("插入元组多余的属性"+s);
            }
        }
        this.tuple.add(newtuple);
    }

    /**
     * 按索引删除某个元组
     * @param index 索引值
     */
    public void deletetuple(int index) throws AccessException {
        if(index>=this.tuple.size())throw new AccessException("索引值超出下标界限");
        this.tuple.remove(index);
    }
    /**
     * 向表中加入某一属性,并检查表的匹配
     * @param field 加入的属性
     * @throws checkException 抛出出错误
     */
    public void addfield(Field field) throws checkException {
        Field.newFieldcheck(field);

        if(!Field.contains(this.fields,field.getFieldname()))
            throw new checkException("尝试插入重复的字段");

        this.fields.add(field);
        Object defaultvalue=field.getDefaultvalue();
        String fieldname=field.getFieldname();
        for(JSONObject jsonObject:tuple){
            jsonObject.put(fieldname,defaultvalue);
        }
        this.checkTuplesFieldname();
    }

    /**
     * 移除表的某个属性
     * @param fieldname 将要溢出的属性名
     * @throws checkException 抛出异常
     */
    public void removefield(String fieldname) throws checkException {

        boolean flag=true;
        int i=0;
        for(Field field:fields){
            if(fieldname.equals(field.getFieldname())){
                fields.remove(i);
                flag=false;
                break;
            }
            i++;
        }

        if(flag){
            throw new checkException("没有找到该字段");
        }

        for(JSONObject jsonObject:tuple){
            jsonObject.remove(fieldname);
        }
        this.checkTuplesFieldname();
    }

    public void FKcheck(ArrayList<Table> tables) throws checkException {
        ArrayList<Field> FKfield=new ArrayList<Field>();

        for(Field field:fields){
            if("FK".equals(field.getKey())){
                FKfield.add(field);
            }
        }

        Iterator<Table> it=tables.iterator();
        for(Field field:FKfield) {
            boolean flag=false;
            Table table = null;

            while (it.hasNext()) {
                table=it.next();
                if(table.getName().equals(field.getFieldname())){
                    flag=true;
                    break;
                }
            }

            if(flag||table==null){
                throw new checkException("未找到外键依赖的表");
            }

            for(JSONObject temp_tuple:tuple) {
                if(table.find(field.getForeignFieldName(), temp_tuple.get(field.getFieldname())).isEmpty()){
                    throw new checkException("外键依赖错误,值"+temp_tuple.get(field.getFieldname()) +"不存在于表"+table.getName());
                }
            }

        }
    }



}
